package com.meshnetics.gb.stack.mac.type;

import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.impl.ByteUtils;
import com.meshnetics.gb.type.impl.custom.StructureDataValue;
import com.meshnetics.gb.type.impl.zcl.*;

import java.nio.ByteBuffer;

/**
 * The PANDescriptor used in MACBeaconNotifyIndication and MACScanConfirm.
 *
 * @author AKovylin
 */
public class PANDescriptor extends StructureDataValue {
    public PANDescriptor(DataAddress coordAddr, UInt logicalChannel, SuperframeSpec superframeSpec,
                         Bool gtsPermit, UInt linkQuality, UInt timestamp,
                         boolean securityUse, int aclEntry, boolean securityFailure) {
        this(coordAddr, logicalChannel, superframeSpec, gtsPermit, linkQuality, timestamp,
                createBitmap(securityUse, aclEntry, securityFailure));

    }

    private static Bitmap createBitmap(boolean securityUse, int aclEntry, boolean securityFailure) {
        if (aclEntry < 0 || aclEntry > 8) {
            throw new IllegalArgumentException("invalid aclEntry");
        }
        BitmapEditor editor = new BitmapEditor(TypeBitmap.BITMAP_8);
        editor.setBit(0, securityUse);
        editor.setBits(1, 5, aclEntry);
        editor.setBit(5, securityFailure);
        return editor.create();
    }

    public PANDescriptor(DataAddress coordAddr, UInt logicalChannel, SuperframeSpec superframeSpec,
                         Bool gtsPermit, UInt linkQuality, UInt timestamp, Bitmap secAndACL) {
        this(TypePANDescriptor.TYPE, new DataValue[]{coordAddr, logicalChannel, superframeSpec,
                gtsPermit, linkQuality, timestamp, secAndACL});
    }

    PANDescriptor(TypePANDescriptor type, DataValue[] values) {
        super(type, values);
        DataAddress coordAddr = (DataAddress) values[0];
        for (int i = 0; i < values.length; i++)
            if (coordAddr.isNoAddress()) {
                throw new IllegalArgumentException("coordAddr must contain short or extended device address");
            }
    }

    public DataAddress getCoordAddress() {
        return (DataAddress) getFieldValue(0);
    }

    public int getLogicalChannel() {
        return ((Number) ((UInt) getFieldValue(1)).toObject()).byteValue() & 0xff;
    }

    public SuperframeSpec getSuperframeSpec() {
        return (SuperframeSpec) getFieldValue(2);
    }

    public boolean isGTSPermit() {
        Boolean b = (Boolean) ((Bool) getFieldValue(3)).toObject();
        return b == null ? false : b.booleanValue();
    }

    public int getLinkQuality() {
        return ((Number) ((UInt) getFieldValue(4)).toObject()).byteValue() & 0xff;
    }

    public int getTimestamp() {
        return ((Number) ((UInt) getFieldValue(5)).toObject()).intValue() & 0xffffff;
    }

    public boolean isSecurityUse() {
        return ((Bitmap) getFieldValue(6)).getBit(0);
    }

    public int getACLEntry() {
        return ((Bitmap) getFieldValue(6)).getBits(1, 5);
    }

    public boolean isSecurityFailure() {
        return ((Bitmap) getFieldValue(6)).getBit(5);
    }

    public int getDataSize() {
        int size = 0;
        for (int i = 0; i < ((TypePANDescriptor) type).getFieldCount(); i++) {
            size += getFieldValue(i).getDataSize();
        }
        return size;
    }

    public Object toObject() {
        throw new UnsupportedOperationException("not implemented yet");
        // todo reimplement
    }

    public void serialize(ByteBuffer bb) {
        ByteUtils.ensureNotNull(bb);
        for (int i = 0; i < ((TypePANDescriptor) type).getFieldCount(); i++) {
            getFieldValue(i).serialize(bb);
        }
    }
}
